package ext.trace.scene;

import ext.trace.camera.Camera;
import ext.trace.geom.Geom;
import java.util.ArrayList;
import math.rand.RNG;
import math.region.Circle3;
import math.region.Cylinder3;
import math.region.Plane3;
import math.region.Ray3;
import math.region.Rect3;
import math.region.Sphere3;
import math.region.Surface3;
import math.region.Tri3;

public abstract class Scene
{
    
    public Camera rCamera;
    
    public ArrayList<Geom> mLights;
    
    
    public Scene()
    {
        this.rCamera = null;
        this.mLights = new ArrayList<>();
    }
    
    
    public void add(final Geom aGeom)
    {
        if (aGeom.mMat.mRadExit > 0.0)
        {
            this.mLights.add(aGeom);
        }
    }
    
    
    protected final double intersectSurface(Surface3 g, Ray3 ray)
    {
        double t = Double.MAX_VALUE;
        if (g instanceof Tri3)
        {
            t = ray.intersect((Tri3) g);
        }
        else if (g instanceof Rect3)
        {
            t = ray.intersect((Rect3) g);
        }
        else if (g instanceof Sphere3)
        {
            t = ray.intersect((Sphere3) g);
        }
        else if (g instanceof Plane3)
        {
            t = ray.intersect((Plane3) g);
        }
        else if (g instanceof Circle3)
        {
            t = ray.intersect((Circle3) g);
        }
        else if (g instanceof Cylinder3)
        {
            t = ray.intersect((Cylinder3) g);
        }
        return t;
    }
    
    /**
    Casts the Ray3 into the Scene. 
    @param aRay Input Ray3 to cast.
    */
    public abstract Hit castRay(Ray3 aRay, double time);
    
    
    /**
    Casts an occlusion ray into this Scene. 
    Returns 1.0 if and only if there is no geometry occluding the target Geom. 
    Returns 0.0 when another Geom occludes the target Geom.
    @param aRay
    @param aTarget
    @return 
    */
    public abstract double castOcclusion(
        Ray3 aRay, 
        Geom aTarget, 
        double time
    );
    
    
    public final Geom randomLight(RNG aRNG)
    {
        int rind = aRNG.integer(this.mLights.size());
        return this.mLights.get(rind);
    }
    
}
